﻿using System;
using System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using GroceryPriceBook.Core.Mappers.Extensions;
using GroceryPriceBook.Model;
using ProductManufacturer = GroceryPriceBook.Core.ViewModel.ProductManufacturer;

namespace GroceryPriceBook.Core.Service
{
	public class ProductManufacturerHandler : BaseHandler<ProductManufacturer>
	{
		public ProductManufacturerHandler()
		{
			ServiceResult = ServiceResultEnum.Failure;
		}

		/// <summary>
		/// Add new ProductManufacturer to the database
		/// </summary>
		/// <param name="obj">ProductManufacturer object to be saved to the database</param>
		/// <returns>success or failure</returns>
		public override ServiceResultEnum Create(ref ProductManufacturer obj)
		{
			// Number of changes as a result of the database change
			NumberChanges = 0;
			try
			{
				// Perform data access using the context
				using (var context = new PriceBookEntities())
				{
					// convert to database object
					var dbObj = obj.ToModel();

					// add to the database and retrieve the updated object back (namely the GUID generated into the Id)
					dbObj = context.ProductMakers.Add(dbObj);

					// commit changes to the database
					NumberChanges = context.SaveChanges();

					// convert the database object back to a presentation object with included changes from the database (if any)
					obj = dbObj.ToViewModel();
				}
			}
			catch (System.InvalidOperationException ex)
			{
				Trace.WriteLine(ex.Message);
			}
			catch (ValidationException ex)
			{
				Trace.WriteLine(ex.Message);
			}
			catch (Exception ex)
			{
				Trace.WriteLine(ex.Message);
			}

			return NumberChanges > 0 ? ServiceResultEnum.Success : ServiceResultEnum.Failure;
		}

		/// <summary>
		/// Update the provided ProductManufacturer object in the database
		/// </summary>
		/// <param name="obj">ProductManufacturer object to be updated in the database</param>
		/// <returns>success or failure</returns>
		public override ServiceResultEnum Update(ref ProductManufacturer obj)
		{
			// Number of changes as a result of the database change
			NumberChanges = 0;
			try
			{
				// Perform data access using the context
				using (var context = new PriceBookEntities())
				{
					// convert to database object
					var dbObj = obj.ToModel();

					// add to the database and retrieve the updated object back (namely the GUID generated into the Id)
					context.Entry(dbObj).State = EntityState.Modified;

					// commit changes to the database
					NumberChanges = context.SaveChanges();

					// convert the database object back to a presentation object with included changes from the database (if any)
					obj = dbObj.ToViewModel();
				}
			}
			catch (System.InvalidOperationException ex)
			{
				Trace.WriteLine(ex.Message);
			}
			catch (ValidationException ex)
			{
				Trace.WriteLine(ex.Message);
			}
			catch (Exception ex)
			{
				Trace.WriteLine(ex.Message);
			}

			return NumberChanges > 0 ? ServiceResultEnum.Success : ServiceResultEnum.Failure;
		}

		/// <summary>
		/// Delete one ProductManufacturer from the database - will error if it is FK'ed to another object
		/// </summary>
		/// <param name="id">id of the ProductManufacturer to delete</param>
		/// <returns>success or failure status</returns>
		public override ServiceResultEnum Delete(Guid id)
		{
			// Number of changes as a result of the database change
			NumberChanges = 0;
			try
			{
				// Perform data access using the context
				using (var context = new PriceBookEntities())
				{
					// convert to database object
					var dbObj = context.ProductMakers.Find(id);

					if (dbObj != null)
					{
						context.ProductMakers.Remove(dbObj);

						// commit changes to the database
						NumberChanges = context.SaveChanges();
					}
				}
			}
			catch (System.InvalidOperationException ex)
			{
				Trace.WriteLine(ex.Message);
			}
			catch (ValidationException ex)
			{
				Trace.WriteLine(ex.Message);
			}
			catch (Exception ex)
			{
				Trace.WriteLine(ex.Message);
			}

			return NumberChanges > 0 ? ServiceResultEnum.Success : ServiceResultEnum.Failure;
		}

		/// <summary>
		/// Retrieve all ProductManufacturers from the database for presentation
		/// </summary>
		/// <returns>list of ProductManufacturers</returns>
		public override List<ProductManufacturer> ReadAll()
		{
			var objList = new List<ProductManufacturer>();
			try
			{
				// Perform data access using the context
				using (var context = new PriceBookEntities())
				{
					// convert to presentation object
					objList = context.ProductMakers.ToList().ToViewModel();
				}
			}
			catch (System.InvalidOperationException ex)
			{
				Trace.WriteLine(ex.Message);
			}
			catch (Exception ex)
			{
				Trace.WriteLine(ex.Message);
			}

			return objList;
		}

		/// <summary>
		/// Retrieve on ProductManufacturer object for presentation
		/// </summary>
		/// <param name="id">id to look up the ProductManufacturer in the database</param>
		/// <returns>presentation ProductManufacturer object or null if not found</returns>
		public override ProductManufacturer ReadOne(Guid id)
		{
			var obj = new ProductManufacturer();
			try
			{
				// Perform data access using the context
				using (var context = new PriceBookEntities())
				{
					// convert to presentation object
					obj = context.ProductMakers.Find(id).ToViewModel();
				}
			}
			catch (System.InvalidOperationException ex)
			{
				Trace.WriteLine(ex.Message);
			}
			catch (Exception ex)
			{
				Trace.WriteLine(ex.Message);
			}

			return obj;
		}

		/// <summary>
		/// Retrieve list of presentation objects filtered by provided object's properties
		/// </summary>
		/// <param name="obj">Presentation object with properties used to filter database query</param>
		/// <returns>List of ProductManufacturer objects filtered and then sorted by name</returns>
		public override List<ProductManufacturer> ReadFiltered(ProductManufacturer obj)
		{
			var objList = new List<ProductManufacturer>();
			try
			{
				// Build dynamic query based on the provided presentation object's properties
				// http://stackoverflow.com/questions/13628748/linq-dynamic-query-for-entity-framework
				var conditions = new List<Func<Model.ProductMaker, bool>>();
				if (!string.IsNullOrEmpty(obj.Name)) { conditions.Add(x => x.Name.Contains(obj.Name)); }

				// Perform data access using the context
				using (var context = new PriceBookEntities())
				{
					var query = context.ProductMakers.AsQueryable();
					query = conditions.Aggregate(query, (current, condition) => current.Where(condition).AsQueryable());

					// convert to presentation object
					objList = query.OrderByDescending(a => a.Name).ToList().ToViewModel();
				}
			}
			catch (System.InvalidOperationException ex)
			{
				Trace.WriteLine(ex.Message);
			}
			catch (Exception ex)
			{
				Trace.WriteLine(ex.Message);
			}

			return objList;
		}
	}
}
